import React, {forwardRef, useEffect, useImperativeHandle, useState} from 'react';
import '../styles/style.css'
import {config} from "../../../config";
import {message, Tag} from "antd";
import {SyncOutlined} from "@ant-design/icons";
import {getUsersPromise} from "../../../api";

/* detect url in a message and add a link tag */
function detectURL(message) {
    var urlRegex = /(((https?:\/\/)|(www\.))[^\s]+)/g;
    return message.replace(urlRegex, function (urlMatch) {
        return '<a href="' + urlMatch + '">' + urlMatch + '</a>';
    })
}

/* ========== */

/* Title component */
function Title(props) {
    if (props.sendeToUser.name) {
        return (
            <div className={"chatApp__convTitle"}>与 {props.sendeToUser.name} 的会话</div>
        )
    } else {
        return (
            <div>
                <Tag icon={<SyncOutlined spin />} color="processing">
                    探❀
                </Tag>
            </div>
        )
    }
}


/* InputMessage component - used to type the message */
function InputMessage(props) {
    let ownerInput = {}
    let ownerAvatarInput = {}
    let messageInput={}

    function handleSendMessage(event) {
        event.preventDefault();
        /* Disable sendMessage if the message is empty */
        if (messageInput.value.length > 0) {
            props.sendMessageLoading(ownerInput.value, props.snedToUser, ownerAvatarInput.value,messageInput.value, props.isGroup);
            /* Reset input after send*/
            messageInput.value = '';
        }
    }

    function handleTyping(event) {
        /* Tell users when another user has at least started to write */
        if (messageInput.value.length > 0) {
            props.typing();
        } else {
            /* When there is no more character, the user no longer writes */
            props.resetTyping();
        }
    }

    /* If the chatbox state is loading, loading class for display */
    let loadingClass = props.isLoading ? 'chatApp__convButton--loading' : '';
    let sendButtonIcon = <i className={"material-icons"}>send</i>;
    let disClass = props.snedToUser.name ? "" : " disable"
    return (
        <form onSubmit={handleSendMessage}>
            <input
                type="hidden"
                ref={owner => (ownerInput = owner)}
                value={props.owner}
            />
            <input
                type="hidden"
                ref={ownerAvatar => (ownerAvatarInput = ownerAvatar)}
                value={props.ownerAvatar}
            />
            <input
                type="text"
                ref={message => (messageInput = message)}
                className={"chatApp__convInput"}
                placeholder="Text message"
                onKeyDown={handleTyping}
                onKeyUp={handleTyping}
                tabIndex="0"
            />
            <div className={'chatApp__convButton ' + loadingClass + disClass} onClick={handleSendMessage}>
                {sendButtonIcon}
            </div>
        </form>
    );
}

/* end InputMessage component */
/* ========== */

/* ========== */

/* TypingIndicator component */
function TypingIndicator(props) {
    let isTyping = props.isTyping
    if (isTyping.isTyping === true && isTyping.sendTo === props.owner && props.snedToUser.name === isTyping.sender) {
        return (
            <div className={"chatApp__convTyping"}>对方正在输入
                <span className={"chatApp__convTypingDot"}/>
            </div>
        );
    }
    return (
        <div className={"chatApp__convTyping"}/>
    );

}

/* end TypingIndicator component */
/* ========== */

/* ========== */

/* MessageList component - contains all messages */
function MessageList(props) {
        return (
            <div>
                <div className={"chatApp__convTimeline"}>
                    {props.messages.slice(0).reverse().map(
                        messageItem => (
                            <MessageItem
                                key={messageItem.id}
                                owner={props.owner}
                                senderAvatar={messageItem.senderAvatar}
                                sender={messageItem.sender}
                                message={messageItem.message}
                                time={messageItem.time}
                                len={1}
                            />
                        )
                    )}
                </div>
            </div>
        )

}

/* end MessageList component */
/* ========== */

/* ========== */

/* MessageItem component - composed of a message and the sender's avatar */
function MessageItem(props) {
    /* message position formatting - right if I'm the author */
    let messagePosition = ((props.owner == props.sender) ? 'chatApp__convMessageItem--right' : 'chatApp__convMessageItem--left');
    let namePosition = ((props.owner == props.sender) ? 'chatApp__convMessageItem_name_right' : 'chatApp__convMessageItem_name_left');
    let timePosition = ((props.owner == props.sender) ? 'chatApp_msg_time_right' : 'chatApp_msg_time_left');
    return (
        <div className={"chatApp__convMessageItem " + messagePosition + " clearfix"}>
            <img src={props.senderAvatar} alt={props.sender} className="chatApp__convMessageAvatar" />
            <div className={namePosition}>
                {props.sender}
            </div>
            <div className={timePosition}>
                {props.time}
            </div>
            <div className="chatApp__convMessageValue" dangerouslySetInnerHTML={{__html: props.message}}>
            </div>
        </div>
    );

}

/* end MessageItem component */
/* ========== */

/* ========== */

/* ChatBox component - composed of Title, MessageList, TypingIndicator, InputMessage */
function ChatBox(props) {
    const [isLoading, setIsLoading] = useState(false)
    /* catch the sendMessage signal and update the loading state then continues the sending instruction */
    function sendMessageLoading(sender,sendTo, senderAvatar, message, isGroup) {
        setIsLoading(true)
        props.sendMessage(sender,sendTo, senderAvatar, message, isGroup);
        setTimeout(() => {
            setIsLoading(false)
        }, 400);
    }
    let stealthClass = props.sendToUser.name ? '' : ' stealth'
        return (
            <div className={"chatApp__conv"}>
                <Title
                    owner={props.owner}
                    sendeToUser={props.sendToUser}
                />
                <MessageList
                    owner={props.owner}
                    messages={props.messages}
                />
                <div className={"chatApp__convSendMessage clearfix" + stealthClass}>
                    <TypingIndicator
                        owner={props.owner}
                        snedToUser={props.sendToUser}
                        isTyping={props.isTyping}
                    />
                    <InputMessage
                        isLoading={isLoading}
                        owner={props.owner}
                        ownerAvatar={props.ownerAvatar}
                        sendMessage={props.sendMessage}
                        isGroup={props.isGroup}
                        snedToUser={props.sendToUser}
                        sendMessageLoading={sendMessageLoading}
                        typing={props.typing}
                        resetTyping={props.resetTyping}
                    />
                </div>
            </div>
        );
}

/* end ChatBox component */
/* ========== */

/* ========== */

/* ChatRoom component - composed of multiple ChatBoxes */
const ChatContent = (props) => {
    let msgKey = "messages_" +  props.sender.name + "-" + props.sendToUser.name
    let resetKey = "isTyping_" +  props.sender.name
    const [messages, setMessages] = useState(JSON.parse(localStorage.getItem(msgKey)) || [])
    // 表示正在输入，并且要通知sendTo
    const [isTyping, setIsTypings] = useState( {sender: props.sender.name,sendTo: props.sendToUser.name, isTyping: false})
    // 按照格式发消息即可
    /*
    * {
    *   // 发送方
        private String sender;
        // 被发送方 ｜ 群聊名称
        private String sendTo;
        // 发送方头像
        private String senderAvatar;
        // 消息
        private String message;
        // 是否群聊
        private Boolean isGroup;
    * }
    * */

    // update hook
    useEffect(()=>{
        bindOnmessage()
    })

    // ws state监控
    useEffect(()=>{
        if (props.ws.readyState === undefined) return
        console.log("当前ws状态：",props.ws.readyState)
    },[props.ws.readyState])

    // display updated hook
    useEffect(()=>{
        updateDisplay()
    },[props.sendToUser.name])

    function updateDisplay() {
        console.log("获取最新数据")
        msgKey = "messages_" +  props.sender.name + "-" + props.sendToUser.name
        let msgs = JSON.parse(localStorage.getItem(msgKey));
        console.log(msgKey,msgs)
        setMessages(msgs || [])
    }

    function bindOnmessage() {
        props.ws.onmessage=onMessage
    }

    // 监听是否正在输入
    useEffect(()=>{
        bindOnmessage()
        if(props.ws.readyState)props.ws.send(JSON.stringify(isTyping))
    }, [isTyping.isTyping])

    function buildNotification(name, message, avatar) {
        let notification = new Notification(`${name}发来了一条消息:`, {
            body: message,
            icon: avatar
        });
        notification.onclick = function () {
            window.focus();
            // 将display 切换
            for (let user of props.users) {
                if (user.name === name) {
                    props.setSendToUser(user)
                    setTimeout(props.newMsgCountDown(name), 1500)
                    break
                }
            }
            notification.close();
        };
    }

    function requestNotification(name, message, avatar) {
        Notification.requestPermission().then(r => {
            if (r === "granted") {
                buildNotification(name, message, avatar);
            } else {
                // 不同意？ 递归调用一直询问就完了
                requestNotification(name, avatar, message)
            }
        })
    }

    const popNotice = function(name, avatar, message) {
        if (Notification.permission === "default") {
            requestNotification(name, message, avatar);
        } else {
            if (Notification.permission !== "denied") {
                buildNotification(name, message, avatar);
            } else {
                // 想拒绝通知？ 不可能
                requestNotification(name, avatar, message)
            }
        }
    };

    function notify(name, avatar, message) {
        if(window.Notification) {
            popNotice(name, avatar, message)
        } else {
            alert("浏览器不支持通知！")
        }
    }

    // 父组件调用改方法改变本地方法结构
    function handleSendMessage(newMessage) {
        let newMsgObj = JSON.parse(newMessage)
        msgKey = "messages_" +  props.sender.name + "-" + newMsgObj.sendTo
        resetKey = "isTyping_" +  props.sender.name
        // 红点计数
        if (newMsgObj.isGroup && newMsgObj.sender !== props.sender.name) {
            props.newMsgCountUp("聊天室")
        } else {
            props.newMsgCountUp(newMsgObj.sender)
        }
        let tmpMsgs = messages
        if (newMsgObj.sendTo === props.sender.name) {
            // 接收方
            // 私聊
            if (!newMsgObj.isGroup) {
                if (props.sendToUser.name === newMsgObj.sender) {
                    // 接收方display 与发送方匹配 正常往messages中添加
                    tmpMsgs.push(newMsgObj)
                    setMessages(tmpMsgs.slice())
                } else {
                    // 这里两个key需要与上面的反过来
                    msgKey = "messages_" +  newMsgObj.sendTo + "-" + newMsgObj.sender
                    resetKey = "isTyping_" +  newMsgObj.sendTo
                    let item = localStorage.getItem(msgKey);
                    if (item === null) {
                        tmpMsgs = []
                    } else {
                        tmpMsgs = JSON.parse(item)
                    }
                    tmpMsgs.push(newMsgObj)
                }
                for (let tmpMsg of tmpMsgs) {
                    if (tmpMsg.sender === newMsgObj.sender) {
                        tmpMsg.senderAvatar = newMsgObj.senderAvatar
                    }
                }
                resetTyping();
                localStorage.setItem(msgKey, JSON.stringify(tmpMsgs))
                localStorage.setItem(resetKey, JSON.stringify(isTyping))
                notify(newMsgObj.sender, newMsgObj.senderAvatar, newMsgObj.message)
            }
        }
        // 发送方
        if (newMsgObj.sender === props.sender.name) {
            // 正常往messages中添加
            tmpMsgs.push(newMsgObj)
            for (let tmpMsg of tmpMsgs) {
                if (tmpMsg.sender === newMsgObj.sender) {
                    tmpMsg.senderAvatar = newMsgObj.senderAvatar
                }
            }
            setMessages(tmpMsgs.slice())
            resetTyping();
            localStorage.setItem(msgKey, JSON.stringify(messages))
            localStorage.setItem(resetKey, JSON.stringify(isTyping))
        } else {
            // 收到群聊消息
            const gItem = localStorage.getItem(msgKey);
            let msgs = gItem === undefined || gItem === null ? [] : JSON.parse(gItem);
            if (props.sendToUser.name === newMsgObj.sendTo) {
                // 当前选择了群聊
                tmpMsgs.push(newMsgObj)
                setMessages(tmpMsgs.slice())
                localStorage.setItem(msgKey, JSON.stringify(tmpMsgs))
            } else {
                msgs.push(newMsgObj)
                localStorage.setItem(msgKey, JSON.stringify(msgs))
            }
            notify(newMsgObj.sender, newMsgObj.senderAvatar, newMsgObj.message)
        }
    }

    /* adds a new message to the chatroom */
    function sendMessage(sender, sendTo, senderAvatar, message, isGroup) {
        setTimeout(() => {
            let messageFormat = (detectURL(message));
            let newMessageItem = {
                sender: sender,
                sendTo: sendTo.name,
                isGroup: sendTo.name === "聊天室",
                senderAvatar: senderAvatar,
                message: messageFormat,
                dirty: false,
                time:props.getTime()
            };
            let ws = props.ws
            ws.onmessage=onMessage
            ws.send(JSON.stringify(newMessageItem))
        }, 100);
    }

    useEffect(()=>{
        // 本地users更新
        getUsersPromise().then((res) => {
            const data = JSON.parse(res)
            genUsers(data.data)
        })
    },[props.state])

    function genUsers(userDatas) {
        let newUsers = []
        for (let userData of userDatas) {
            const user = {
                name: userData.loginName,
                state: userData.state,
                avatar: config.baseUrl + userData.avatar,
                dis: userData.distance
            }
            // if (user.name !== props.sender.name)
            //  props.addUser(user);
            newUsers.push(user)
            // update message' avatar
        }
        props.setUsers(newUsers)
    }

    const onMessage = (e)=> {
        let data = JSON.parse(e.data);
        if (data.hasOwnProperty("isTyping")) {
            setIsTypings(data)
        } else if(data.hasOwnProperty("messageType")) {
            if (data.messageType === 'Open') {
                // props.clearUsers()
                let userDatas = JSON.parse(data.message)
                genUsers(userDatas);
                // setDefaultSentToUser()
            }
        } else {
            handleSendMessage(e.data)
        }
    }

    function typing() {
        let obj = isTyping
        setIsTypings({sender: obj.sender, sendTo: props.sendToUser.name, isTyping: true})
    }

    /* hide the writing indicator */
    function resetTyping() {
        let obj = isTyping
        setIsTypings({sender: obj.sender, sendTo: props.sendToUser.name, isTyping: false})
    }

    /* updates the writing indicator if not already displayed */
    let users = {};
    let chatBoxes = [];
    let sendToUser = props.sendToUser
    let isGroup = props.isGroup
    /* user details - can add as many users as desired */
    users[0] = props.sender;

    /* creation of a chatbox for each user present in the chatroom */
    Object.keys(users).map(function (key) {
        let user = users[key];
        chatBoxes.push(
            <ChatBox
                key={key}
                owner={user.name}
                //groupName={"与" + messages[0].sender + "的聊天"}
                ownerAvatar={user.avatar}
                sendMessage={sendMessage}
                isGroup={isGroup}
                sendToUser={sendToUser}
                typing={typing}
                resetTyping={resetTyping}
                messages={messages}
                isTyping={isTyping}
            />
        );
    })
    users[1] = {name: '聊天室'}

    return (
        <div className={"chatApp__room"}>
            {chatBoxes}
        </div>
    );
}

/* end ChatRoom component */
/* ========== */
export default ChatContent;

